//
// DotNetNuke - http://www.dotnetnuke.com
// Copyright (c) 2002-2010
// by Perpetual Motion Interactive Systems Inc. ( http://www.perpetualmotion.ca )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
//

using System;
using System.Text.RegularExpressions;
using System.Web;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Tabs;
using DotNetNuke.Framework.Providers;
using DotNetNuke.HttpModules;
using System.Collections.Generic;

namespace DotNetNuke.Services.Url.FriendlyUrl
{
	public class DNNFriendlyUrlProvider : FriendlyUrlProvider
	{
		private const string ProviderType = "friendlyUrl";
		private const string RegexMatchExpression = "[^a-zA-Z0-9 ]";
		private ProviderConfiguration _providerConfiguration = ProviderConfiguration.GetProviderConfiguration(ProviderType);
		private bool _includePageName;
		private string _regexMatch;
		private string _fileExtension;
		private UrlFormatType _urlFormat = UrlFormatType.SearchFriendly;

        public DNNFriendlyUrlProvider()
		{
			Provider objProvider = (Provider)_providerConfiguration.Providers[_providerConfiguration.DefaultProvider];
			if (!String.IsNullOrEmpty(objProvider.Attributes["includePageName"]))
			{
				_includePageName = bool.Parse(objProvider.Attributes["includePageName"]);
			} else {
				_includePageName = true;
			}
			if (!String.IsNullOrEmpty(objProvider.Attributes["regexMatch"])) {
				_regexMatch = objProvider.Attributes["regexMatch"];
			} else {
				_regexMatch = RegexMatchExpression;
			}
			if (!String.IsNullOrEmpty(objProvider.Attributes["fileExtension"])) {
				_fileExtension = objProvider.Attributes["fileExtension"];
			} else {
				_fileExtension = ".aspx";
			}
			if (!String.IsNullOrEmpty(objProvider.Attributes["urlFormat"])) {
				switch (objProvider.Attributes["urlFormat"].ToLower()) {
					case "searchfriendly":
						_urlFormat = UrlFormatType.SearchFriendly;
						break;
					case "humanfriendly":
						_urlFormat = UrlFormatType.HumanFriendly;
						break;
					default:
						_urlFormat = UrlFormatType.SearchFriendly;
						break;
				}
			}
		}
		public string FileExtension {
			get { return _fileExtension; }
		}
		public bool IncludePageName {
			get { return _includePageName; }
		}
		public string RegexMatch {
			get { return _regexMatch; }
		}
		public UrlFormatType UrlFormat {
			get { return _urlFormat; }
		}
		public override string FriendlyUrl(TabInfo tab, string path)
		{
			PortalSettings _portalSettings = PortalController.GetCurrentPortalSettings();
			return FriendlyUrl(tab, path, Common.Globals.glbDefaultPage, _portalSettings);
		}
		public override string FriendlyUrl(TabInfo tab, string path, string pageName)
		{
			PortalSettings _portalSettings = PortalController.GetCurrentPortalSettings();
			return FriendlyUrl(tab, path, pageName, _portalSettings);
		}
		public override string FriendlyUrl(TabInfo tab, string path, string pageName, PortalSettings settings)
		{
			return FriendlyUrl(tab, path, pageName, settings.PortalAlias.HTTPAlias);
		}

        public override string FriendlyUrl(TabInfo tab, string path, string pageName, string portalAlias)
        {

            string friendlyPath = path;
            string matchString = "";
            bool isPagePath = tab != null;

            if ((UrlFormat == UrlFormatType.HumanFriendly))
            {
                if ((tab != null))
                {
                    Dictionary<string, string> queryStringDic = GetQueryStringDictionary(path);
                    if ((queryStringDic.Count == 0 || (queryStringDic.Count == 1 && queryStringDic.ContainsKey("tabid"))))
                    {
                        //Return AddHTTP(portalAlias & "/" & tab.TabPath.Replace("//", "/").TrimStart(Convert.ToChar("/")) + ".aspx")
                        friendlyPath = GetFriendlyAlias("~/" + tab.TabPath.Replace("//", "/").TrimStart('/') + ".aspx", portalAlias, isPagePath);
                    }
					else if (queryStringDic.Count == 2 && queryStringDic.ContainsKey("tabid") && queryStringDic.ContainsKey("language"))
					{
                        if(!tab.IsNeutralCulture)
                        {
                        	friendlyPath =
                        		GetFriendlyAlias("~/" + tab.CultureCode + "/" + tab.TabPath.Replace("//", "/").TrimStart('/') + ".aspx",
                        			portalAlias, isPagePath).ToLower();
                        }
                        else
                        {
                        	friendlyPath = 
								GetFriendlyAlias("~/" + queryStringDic["language"] + "/" + tab.TabPath.Replace("//", "/").TrimStart('/') + ".aspx",
									portalAlias, isPagePath).ToLower();
                        }
					}
                    else
                    {
                        if (queryStringDic.ContainsKey("ctl") && !queryStringDic.ContainsKey("language"))
                        {
                            switch (queryStringDic["ctl"])
                            {
                                case "terms":
                                    //Return AddHTTP(portalAlias & "/" & sesMatch.Groups(2).Value & ".aspx")
                                    friendlyPath = GetFriendlyAlias("~/terms.aspx", portalAlias, isPagePath);
                                    break;
                                case "privacy":
                                    //Return AddHTTP(portalAlias & "/" & sesMatch.Groups(2).Value & ".aspx")
                                    friendlyPath = GetFriendlyAlias("~/privacy.aspx", portalAlias, isPagePath);
                                    break;
                                case "login":
                                    if ((queryStringDic.ContainsKey("returnurl")))
                                    {
                                        //Return AddHTTP(portalAlias & "/" & sesMatch.Groups(2).Value & ".aspx?ReturnUrl=" & sesMatch.Groups(4).Value)
                                        friendlyPath = GetFriendlyAlias("~/login.aspx?ReturnUrl=" + queryStringDic["returnurl"], portalAlias, isPagePath);
                                    }
                                    else
                                    {
                                        //Return AddHTTP(portalAlias & "/" & sesMatch.Groups(2).Value & ".aspx")
                                        friendlyPath = GetFriendlyAlias("~/login.aspx", portalAlias, isPagePath);
                                    }
                                    break;
                                case "register":
                                    if ((queryStringDic.ContainsKey("returnurl")))
                                    {
                                        //Return AddHTTP(portalAlias & "/" & sesMatch.Groups(2).Value & ".aspx?ReturnUrl=" & sesMatch.Groups(4).Value)
                                        friendlyPath = GetFriendlyAlias("~/register.aspx?returnurl=" + queryStringDic["returnurl"], portalAlias, isPagePath);
                                    }
                                    else
                                    {
                                        //Return AddHTTP(portalAlias & "/" & sesMatch.Groups(2).Value & ".aspx")
                                        friendlyPath = GetFriendlyAlias("~/register.aspx", portalAlias, isPagePath);
                                    }
                                    break;
                                default:
                                    //Return Search engine friendly version
                                    return GetFriendlyQueryString(tab, GetFriendlyAlias(path, portalAlias, isPagePath), pageName);
                            }
                        }
                        else
                        {
                            //Return Search engine friendly version
                            return GetFriendlyQueryString(tab, GetFriendlyAlias(path, portalAlias, isPagePath), pageName);
                        }
                    }
                }
            }
            else
            {
                //Return Search engine friendly version
                friendlyPath = GetFriendlyQueryString(tab, GetFriendlyAlias(path, portalAlias, isPagePath), pageName);
            }

            friendlyPath = CheckPathLength(friendlyPath, path);

            return friendlyPath;

        }

		private string AddPage(string path, string pageName)
		{
			string friendlyPath = path;
			if ((friendlyPath.EndsWith("/"))) {
				friendlyPath = friendlyPath + pageName;
			} else {
				friendlyPath = friendlyPath + "/" + pageName;
			}
			return friendlyPath;
		}

        private string CheckPathLength(string friendlyPath, string originalpath)
        {
            if (friendlyPath.Length >= 260)
            {
                return Common.Globals.ResolveUrl(originalpath);
            }
            else
            {
                return friendlyPath;
            }
        }

		private string GetFriendlyAlias(string path, string portalAlias, bool IsPagePath)
		{
			string friendlyPath = path;
			string matchString = "";
			if (portalAlias != Null.NullString) {
				if (HttpContext.Current.Items["UrlRewrite:OriginalUrl"] != null) {
                    string httpAlias = Globals.AddHTTP(portalAlias).ToLowerInvariant();
                    string originalUrl = HttpContext.Current.Items["UrlRewrite:OriginalUrl"].ToString().ToLowerInvariant();
                    httpAlias = Globals.AddPort(httpAlias, originalUrl);
                    if (originalUrl.StartsWith(httpAlias))
                    {
                        matchString = httpAlias;
					}
					if ((String.IsNullOrEmpty(matchString))) 
					{
						Match portalMatch = Regex.Match(originalUrl, "^?alias=" + portalAlias, RegexOptions.IgnoreCase);
						if (!object.ReferenceEquals(portalMatch, Match.Empty)) {
                            matchString = httpAlias;
						}
					}

					if ((String.IsNullOrEmpty(matchString))) 
					{
                        //Manage the special case of child portals 
                        //http://www.domain.com/child/default.aspx
                        string tempurl  = HttpContext.Current.Request.Url.Host + DotNetNuke.Common.Globals.ResolveUrl(friendlyPath);
                       if( tempurl.Contains(portalAlias))
					   {
                           matchString = httpAlias;
                       }
                    }

                    if ((String.IsNullOrEmpty(matchString))) 
					{
                        // manage the case where the current hostname is www.domain.com and the portalalias is domain.com
                        // (this occurs when www.domain.com is not listed as portal alias for the portal, but domain.com is)
					    string wwwHttpAlias = Globals.AddHTTP("www." + portalAlias);
                        if (originalUrl.StartsWith(wwwHttpAlias))
                        {
                            matchString = wwwHttpAlias;
                        }
					}
				}
			}
			if ((!String.IsNullOrEmpty(matchString))) {
				if ((path.IndexOf("~") != -1)) {
                    if (matchString.EndsWith("/"))
                        friendlyPath = friendlyPath.Replace("~/", matchString);
                    else
                        friendlyPath = friendlyPath.Replace("~", matchString);
				} else {
					friendlyPath = matchString + friendlyPath;
				}
			} else {
				friendlyPath = Common.Globals.ResolveUrl(friendlyPath);
			}
			if (friendlyPath.StartsWith("//") && IsPagePath) {
				friendlyPath = friendlyPath.Substring(1);
			}
			return friendlyPath;
		}
		private string GetFriendlyQueryString(TabInfo tab, string path, string pageName)
		{
			string friendlyPath = path;
			Match queryStringMatch = Regex.Match(friendlyPath, "(.[^\\\\?]*)\\\\?(.*)", RegexOptions.IgnoreCase);
			string queryStringSpecialChars = "";
			if (!object.ReferenceEquals(queryStringMatch, Match.Empty)) {
				friendlyPath = queryStringMatch.Groups[1].Value;
				friendlyPath = Regex.Replace(friendlyPath, Common.Globals.glbDefaultPage, "", RegexOptions.IgnoreCase);
				string queryString = queryStringMatch.Groups[2].Value.Replace("&amp;", "&");
				if ((queryString.StartsWith("?"))) {
					queryString = queryString.TrimStart(Convert.ToChar("?"));
				}
				string[] nameValuePairs = queryString.Split(Convert.ToChar("&"));
				for (int i = 0; i <= nameValuePairs.Length - 1; i++) {
					string pathToAppend = "";
					string[] pair = nameValuePairs[i].Split(Convert.ToChar("="));
					if ((friendlyPath.EndsWith("/"))) {
						pathToAppend = pathToAppend + pair[0];
					} else {
						pathToAppend = pathToAppend + "/" + pair[0];
					}
					if ((pair.Length > 1)) {
						if ((!String.IsNullOrEmpty(pair[1]))) {
							if ((Regex.IsMatch(pair[1], _regexMatch) == false)) {
								if ((pair[0].ToLower() == "tabid")) {
									if ((Regex.IsMatch(pair[1],"^\\d+$"))) {
										if (tab != null) {
											int tabId = Convert.ToInt32(pair[1]);
											if ((tab.TabID == tabId)) {
												if ((tab.TabPath != Null.NullString) && IncludePageName) {
													pathToAppend = tab.TabPath.Replace("//", "/").TrimStart('/') + "/" + pathToAppend;
												}
											}
										}
									}
								}
								pathToAppend = pathToAppend + "/" + System.Web.HttpUtility.UrlPathEncode(pair[1]);
							} else {
								if (String.IsNullOrEmpty(queryStringSpecialChars))
								{
									queryStringSpecialChars = pair[0] + "=" + pair[1];
								} else {
									queryStringSpecialChars = queryStringSpecialChars + "&" + pair[0] + "=" + pair[1];
								}
								pathToAppend = "";
							}
						} else {
							pathToAppend = pathToAppend + "/" + System.Web.HttpUtility.UrlPathEncode((' ').ToString());
						}
					}
					friendlyPath = friendlyPath + pathToAppend;
				}
			}
			if ((!String.IsNullOrEmpty(queryStringSpecialChars))) {
				return AddPage(friendlyPath, pageName) + "?" + queryStringSpecialChars;
			} else {
				return AddPage(friendlyPath, pageName);
			}
		}

        private Dictionary<string, string> GetQueryStringDictionary(string path)
        {
            string[] parts = path.ToLowerInvariant().Split('?');
            Dictionary<string, string> results = new Dictionary<string, string>();


            if ((parts.Length == 2))
            {
                foreach (string part in parts[1].Split('&'))
                {
                    string[] keyvalue = part.Split('=');
                    if ((keyvalue.Length == 2))
                    {
                        results[keyvalue[0]] = keyvalue[1];
                    }
                }

            }

            return results;

        }
	}
}
